package util

import (
	"bytes"
	"encoding/hex"
	"strings"
	"unicode"
	"unicode/utf8"
)

type Type struct {
	types map[string]int
	names map[int]string
}

func CreateType() *Type {
	return &Type{names: make(map[int]string), types: make(map[string]int)}
}

func (t *Type) Register(n string, v int) {
	t.types[n] = v
	t.names[v] = n
}

func (t *Type) String(v int) (string, bool) {
	n, ok := t.names[v]
	return n, ok
}

func (t *Type) Create(n string) (int, bool) {
	v, ok := t.types[n]
	return v, ok
}

func (t *Type) List() []int {
	list := make([]int, 0)
	for t := range t.names {
		list = append(list, t)
	}
	return list
}

// 0x..., 0X..., ...
func HexToBytes(s string) ([]byte, error) {
	if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") {
		s = s[2:]
	}
	return hex.DecodeString(s)
}

func BytesToHexWith(ds []byte, prefix bool, width int) string {
	ll := len(ds)

	sb := &strings.Builder{}
	if prefix {
		sb.WriteString("0x")
	}
	for i := 0; i < (width - ll*2); i++ {
		sb.WriteString("0")
	}
	sb.WriteString(hex.EncodeToString(ds))
	return sb.String()
}

func IsLetter(ch rune) bool {
	return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
}

func IsNumber(ch rune) bool {
	return '0' <= ch && ch <= '9'
}

func SplitItem(s string) []string {
	if len(s) == 0 {
		return []string{}
	}
	list := []string{}

	slist := []rune(s)
	ts := []rune(nil)
	isName := false
	for i := 0; i < len(slist); i++ {
		item := slist[i]
		if i == 0 {
			isName = (IsLetter(item) || IsNumber(item))
			ts = []rune{item}
		} else {
			isn := (IsLetter(item) || IsNumber(item))
			if isn == isName {
				ts = append(ts, item)
			} else {
				list = append(list, string(ts))

				isName = isn
				ts = []rune{item}
			}
		}
	}
	if len(ts) > 0 {
		list = append(list, string(ts))
	}
	newList := make([]string, 0)
	for i := 0; i < len(list); i++ {
		item := list[i]
		item = strings.TrimSpace(item)
		if len(item) > 0 {
			newList = append(newList, item)
		}
	}
	return newList
}

// return empty when empty
func Split(s, sep string) []string {
	if len(s) == 0 {
		return []string{}
	}
	return strings.Split(s, sep)
}

func Title(s string) string {
	if len(s) > 0 {
		list := []rune(s)
		ll := len(list)
		newList := make([]rune, ll)
		for i := 0; i < ll; i++ {
			if i == 0 {
				newList[i] = unicode.ToUpper(list[i])
			} else {
				newList[i] = unicode.ToLower(list[i])
			}
		}
		return string(newList)
	}
	return s
}

func GetName(n string) string {
	list := Split(n, "_")
	ll := len(list)
	switch ll {
	case 0:
		panic("error contract name")
	case 1:
		return Title(list[0])
	default:
		newList := make([]string, ll)
		for i := 0; i < ll; i++ {
			item := list[ll-1-i]
			if i == 0 {
				newList[i] = Title(item)
			} else {
				newList[i] = item
			}
		}
		return strings.Join(newList, "_")
	}
}

func PaddingZeroInLeft(data []byte, N int) []byte {
	ll := len(data)
	return append(bytes.Repeat([]byte{0x0}, N-ll), data...)
}
